// Copyright 2011 Jim Xochellis
// Licensed under the Apache License, Version 2.0 
// (http://www.apache.org/licenses/LICENSE-2.0)
 
#include "prof_value.h"
#include "prof_iterator.h"

#include <vector>
#include <algorithm>
#include <iostream>

//---------------------------------------------------------------
namespace generic_profiling
{

//Bellow follows a small indicative collection of algorithms
//The following algorithms have been derived from the SGI STL v3.3
//http://www.sgi.com/tech/stl/

template <typename _Tp>
inline void swap(_Tp& __a, _Tp& __b)
{
	_Tp __tmp = __a;
	__a = __b;
	__b = __tmp;
}

template<typename _ForwardIter1, typename _ForwardIter2>
inline void iter_swap(_ForwardIter1 __a, _ForwardIter2 __b)
{
	swap(*__a, *__b);
}

template <class _InputIter, class _Tp>
typename std::iterator_traits<_InputIter>::difference_type
count(_InputIter __first, _InputIter __last, const _Tp& __val)
{
	typename std::iterator_traits<_InputIter>::difference_type __n = 0;

	for ( ; __first != __last; ++__first)
	{
		if (*__first == __val)
			++__n;
	}

	return __n;
}

template <class _ForwardIter>
_ForwardIter min_element(_ForwardIter __first, _ForwardIter __last)
{
	if (__first == __last)
		return __first;
	
	_ForwardIter __result = __first;
	while (++__first != __last)
	{
		if (*__first < *__result)
			__result = __first;
	}

	return __result;
}

template<typename _InputIterator, typename _Tp>
_Tp accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
{
	for ( ; __first != __last; ++__first)
		__init = __init + *__first;

	return __init;
}

//---------------------------------------------------------------

static void profile_counters_report()
{
	for (unsigned counter_index = 0; counter_index < profiling_events_count; ++counter_index)
	{
		size_t counter_value = generic_profiling::get_event_counter(counter_index);

		if (counter_value > 0)
			std::cout << "<" << generic_profiling::get_event_name(counter_index) << ">"
						<< " counter: " << counter_value << std::endl;
	}
}

//---------------------------------------------------------------

void prof_sanity_test(size_t test_size)
{
	typedef generic_profiling::prof_value<int> test_value;
	typedef std::vector<test_value> prof_container;
	typedef generic_profiling::prof_iterator<prof_container::iterator> prof_iterator;

	prof_container test_container(test_size);

	prof_iterator begin_iterator(test_container.begin());
	prof_iterator end_iterator(test_container.end());

	std::cout << "swap() profiling report:\n\n";
	{
		test_value val1(1), val2(2);

		generic_profiling::reset_event_counters();
		generic_profiling::swap(val1, val2);
		profile_counters_report();
	}
	std::cout << "----------\n\n";

	std::cout << "custom fill-loop #1:\n\n";
	{
		generic_profiling::reset_event_counters();
		for (prof_iterator it = begin_iterator; it != end_iterator; ++it)
			*it = 0;
		profile_counters_report();
	}
	std::cout << "----------\n\n";

	std::cout << "custom fill-loop #2:\n\n";
	{
		generic_profiling::reset_event_counters();
		for (size_t ii = 0; ii < test_container.size(); ++ii)
			begin_iterator[ii] = (prof_container::value_type)ii;
		profile_counters_report();
	}
	std::cout << "----------\n\n";

	std::cout << "iter_swap() profiling report:\n\n";
	{
		prof_iterator aux_iterator(test_container.begin()+1);
		generic_profiling::reset_event_counters();
		generic_profiling::iter_swap(begin_iterator, aux_iterator);
		profile_counters_report();
	}
	std::cout << "----------\n\n";

	std::cout << "count() profiling report:\n\n";
	{
		test_value val(1);
		generic_profiling::reset_event_counters();
		generic_profiling::count(begin_iterator, end_iterator, val);
		profile_counters_report();
	}
	std::cout << "----------\n\n";

	std::cout << "accumulate() profiling report:\n\n";
	{
		test_value val(1);
		generic_profiling::reset_event_counters();
		val = generic_profiling::accumulate(begin_iterator, end_iterator, val);
		profile_counters_report();
	}
	std::cout << "----------\n\n";

}

} //namespace generic_profiling
